﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dycox.Workflows
{
    internal class EntranceExecutionContext : WorkflowForwardingContext
    {
        public EntranceExecutionContext(Workflow workflow, OperationArguments args)
            : base(workflow, args)
        {

        }

        internal override void BeginExecute()
        {
            base.BeginExecute();

            this.Workflow.Control.ValidateEnteringStage(Process, Workflow.Entrance, AppObject);
        }

        protected override IWorkflowComponent GetContextComponent()
        {
            var ctl = Workflow.Control;
            WFProcess proc;

            if (!Arguments.ComponentID.IsEmpty)
            {
                proc = DataProvider.GetProcess(Arguments.ComponentID);
            }
            else
                proc = null;

            proc = ctl.CreateProcess(proc);

            DataProvider.Insert(proc);

            return ctl.CreateStep(proc.MainThread, Workflow.Entrance);
        }

        public override bool IsNewThread
        {
            get
            {
                return true;
            }
        }

        protected override IReflectionRoot GetAppObject()
        {
            IRequiresWorkflowReflection rr = this.Workflow as IRequiresWorkflowReflection;

            if (rr != null)
                return rr.CreateAppObject(Arguments);
            else
                return null;
        }
    }

    internal class InjectionExecutionContext : WorkflowForwardingContext
    {
        InjectionStage _stage;

        public InjectionExecutionContext(InjectionStage stage, OperationArguments args)
            : base(stage.Workflow, args)
        {
            _stage = stage;
        }

        internal override void BeginExecute()
        {
            base.BeginExecute();

            
        }

        protected override IWorkflowComponent GetContextComponent()
        {
            ComponentIdentifier id = Arguments.ComponentID;
            WFThread thread;

            if (_stage.Mode == InjectionMode.Thread)
            {

                IWorkflowComponent wfc = DataProvider.GetComponent(id, true);

                Stage dep = _stage.GetDependentStage();
                WFStep step = null;

                if (wfc is WFStep)
                    thread = ((WFStep)wfc).Thread;
                else if (wfc is WFThread)
                    thread = (WFThread)wfc;
                else
                {
                    WFProcess proc = (WFProcess)wfc;
                    step = proc.ThreadsInternal.SelectMany(o => o.StepsInternal)
                        .Where(o => o.Status == WorkflowComponentStatus.Completed && o.StageName == dep.Name)
                        .OrderByDescending(o => o.FinishTime)
                        .FirstOrDefault();

                    if (step == null)
                        throw new WorkflowException(SR.GetString("dependent_step_not_found"));

                    thread = step.Thread;
                }

                WFStep active = thread.ActiveStep;

                if (active != null && active.StageName == _stage.Name)
                {
                    return active;
                }

                if (step == null)
                {
                    step = (thread.ActiveStep ?? thread.LastStep).FindNearestStep(dep.Name);
                    if (step == null)
                        throw new WorkflowException(SR.GetString("locate_incoming_stage_failure"));
                }

                thread = Workflow.Control.CreateChildThread(step);
            }
            else
            {
                WFProcess proc = (WFProcess)DataProvider.GetComponent(new ComponentIdentifier(id.ProcessId), true);
                thread = Workflow.Control.CreateChildThread(proc.MainThread);
            }

            return Workflow.Control.CreateStep(thread, _stage);
        }

        public override bool IsNewThread
        {
            get
            {
                return true;
            }
        }
    }
}
